// Copyright 2022 a.o.smith ALL Rights Reserved
package com.liuxinlong.modules.chimi.service.impl;

import com.liuxinlong.common.AosException;
import com.liuxinlong.common.ExcelData;
import com.liuxinlong.common.SnowFlake;
import com.liuxinlong.constant.ThreadLocalConstants;
import com.liuxinlong.constant.TimeFormatConstants;
import com.liuxinlong.enums.BarPointBusinessEnum;
import com.liuxinlong.enums.BarPointSourceEnum;
import com.liuxinlong.enums.ReturnCodes;
import com.liuxinlong.modules.chimi.service.BarLuckDetailService;
import com.liuxinlong.modules.chimi.service.BarLuckService;
import com.liuxinlong.modules.chimi.service.BarPointsService;
import com.liuxinlong.modules.dao.BarLuckAudienceDao;
import com.liuxinlong.modules.dao.BarLuckAwardDao;
import com.liuxinlong.modules.dao.BarLuckDao;
import com.liuxinlong.modules.dao.BarLuckFlowDao;
import com.liuxinlong.modules.dao.BarPointsFlowDao;
import com.liuxinlong.modules.dao.BarPresentDao;
import com.liuxinlong.modules.dao.BarPresentFlowDao;
import com.liuxinlong.modules.dao.UserDao;
import com.liuxinlong.modules.entity.BarLuck;
import com.liuxinlong.modules.entity.BarLuckAudience;
import com.liuxinlong.modules.entity.BarLuckAward;
import com.liuxinlong.modules.entity.BarLuckFlow;
import com.liuxinlong.modules.entity.BarPointsFlow;
import com.liuxinlong.modules.entity.BarPresent;
import com.liuxinlong.modules.entity.BarPresentFlow;
import com.liuxinlong.modules.entity.User;
import com.liuxinlong.utils.ExcelUtils;
import com.liuxinlong.utils.ObjectUtils;
import com.liuxinlong.utils.ThreadlocalUtils;
import com.liuxinlong.utils.TimeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 论坛活动管理服务层接口实现
 *
 * @author liuxinlong@hotwater.com.cn
 * @version 1.0.0
 * @since 2022-08-08
 */
@Service
@Slf4j
public class BarLuckServiceImpl implements BarLuckService {

    @Autowired
    private BarLuckDao barLuckDao;

    @Autowired
    private BarLuckAudienceDao barLuckAudienceDao;

    @Autowired
    private BarLuckAwardDao barLuckAwardDao;

    @Autowired
    private BarLuckFlowDao barLuckFlowDao;

    @Autowired
    private UserDao userDao;

    @Autowired
    private BarLuckDetailService barLuckDetailService;

    @Autowired
    private BarPresentFlowDao barPresentFlowDao;

    @Autowired
    private BarPointsFlowDao barPointsFlowDao;

    @Autowired
    private BarPointsService barPointsService;

    @Autowired
    private BarPresentDao barPresentDao;

    @Override
    public List<Map<String, Object>> pageLuck(Map<String, Object> queryParam) {
        int startNum = (int) queryParam.get("startNum");
        List<BarLuck> originalList = barLuckDao.pageBarLuckList(queryParam);
        return completeResult(originalList, startNum);
    }

    @Override
    public int getLuckCount(Map<String, Object> queryParam) {
        return barLuckDao.getBarLuckCount(queryParam);
    }

    @Override
    public void addLuck(BarLuck barLuck) {
        BarLuck barLuckDb = barLuckDao.getBarLuckBySn(barLuck.getSn());
        if (!ObjectUtils.isEmpty(barLuckDb)) {
            throw new AosException(ReturnCodes.HTTP_PARAM_ERROR, "新增活动信息异常，活动编码重复！");
        }
        barLuck.setId(SnowFlake.nextIdStr());
        String currentTime = TimeUtils.getDate(TimeFormatConstants.YYYY_MM_DD_HH_MM_SS);
        barLuck.setCreateTime(currentTime);
        User currentUser = (User) ThreadlocalUtils.getCache(ThreadLocalConstants.USER_INFO);
        barLuck.setCreateUser(currentUser.getId());
        barLuckDao.insert(barLuck);
    }

    @Override
    public void updateLuck(BarLuck barLuck) {
        String id = barLuck.getId();
        if (StringUtils.isEmpty(id)) {
            throw new AosException(ReturnCodes.HTTP_PARAM_ERROR, "修改活动信息异常，活动不存在！");
        }
        BarLuck oldInfo = barLuckDao.selectById(id);
        if (ObjectUtils.isEmpty(oldInfo)) {
            throw new AosException(ReturnCodes.HTTP_PARAM_ERROR, "修改活动信息异常，活动不存在！");
        }
        if (oldInfo.getStatus() == 1) {
            throw new AosException(ReturnCodes.HTTP_PARAM_ERROR, "修改活动信息异常，活动已启用！");
        }
        BarLuck barLuckDb = barLuckDao.getBarLuckBySn(barLuck.getSn());
        if (!ObjectUtils.isEmpty(barLuckDb) && !StringUtils.equals(id, barLuckDb.getId())) {
            throw new AosException(ReturnCodes.HTTP_PARAM_ERROR, "新增活动信息异常，活动编码重复！");
        }
        String currentTime = TimeUtils.getDate(TimeFormatConstants.YYYY_MM_DD_HH_MM_SS);
        barLuck.setUpdateTime(currentTime);
        User currentUser = (User) ThreadlocalUtils.getCache(ThreadLocalConstants.USER_INFO);
        barLuck.setUpdateUser(currentUser.getId());
        barLuckDao.updateById(barLuck);
    }

    @Override
    public void deleteLuck(String id) {
        if (StringUtils.isEmpty(id)) {
            throw new AosException(ReturnCodes.HTTP_PARAM_ERROR, "删除活动信息异常，活动不存在！");
        }
        BarLuck oldInfo = barLuckDao.selectById(id);
        if (ObjectUtils.isEmpty(oldInfo)) {
            throw new AosException(ReturnCodes.HTTP_PARAM_ERROR, "删除活动信息异常，活动不存在！");
        }
        if (oldInfo.getStatus() == 1) {
            throw new AosException(ReturnCodes.HTTP_PARAM_ERROR, "删除活动信息异常，活动已启用！");
        }
        Map<String, Object> queryParam = new HashMap<>();
        queryParam.put("luckId", id);
        int flowCount = barLuckFlowDao.getBarLuckFlowCount(queryParam);
        if (flowCount > 0) {
            throw new AosException(ReturnCodes.HTTP_PARAM_ERROR, "删除活动信息异常，已存在活动参与记录！");
        }
        barLuckDao.deleteById(id);
    }

    @Override
    public void updateLuckStatus(String id) {
        if (StringUtils.isEmpty(id)) {
            throw new AosException(ReturnCodes.HTTP_PARAM_ERROR, "修改活动状态异常，活动不存在！");
        }
        BarLuck oldInfo = barLuckDao.selectById(id);
        if (ObjectUtils.isEmpty(oldInfo)) {
            throw new AosException(ReturnCodes.HTTP_PARAM_ERROR, "修改活动状态异常，活动不存在！");
        }
        int targetStatus = oldInfo.getStatus() == 0 ? 1 : 0;
        if (targetStatus == 1) {
            Map<String, Object> queryParam = new HashMap<>();
            queryParam.put("luckId", id);
            queryParam.put("startNum", 0);
            queryParam.put("pageSize", 1000);
            if (oldInfo.getDelivery() == 1) {
                int audienceCount = barLuckAudienceDao.getBarLuckAudienceCount(queryParam);
                if (audienceCount == 0) {
                    throw new AosException(ReturnCodes.HTTP_PARAM_ERROR, "活动启用失败，目标用户未配置！");
                }
            }
            int awardCount = barLuckAwardDao.getBarLuckAwardCount(queryParam);
            if (awardCount == 0) {
                throw new AosException(ReturnCodes.HTTP_PARAM_ERROR, "活动启用失败，奖品未配置！");
            }
        }
        barLuckDao.updateLuckStatus(id, targetStatus);
    }

    @Override
    @Transactional
    public void copyLuck(BarLuck barLuck, String subInfo) {
        BarLuck barLuckDb = barLuckDao.getBarLuckBySn(barLuck.getSn());
        if (!ObjectUtils.isEmpty(barLuckDb)) {
            throw new AosException(ReturnCodes.HTTP_PARAM_ERROR, "新增活动信息异常，活动编码重复！");
        }
        String mainId = barLuck.getId();
        String id = SnowFlake.nextIdStr();
        barLuck.setId(id);
        String currentTime = TimeUtils.getDate(TimeFormatConstants.YYYY_MM_DD_HH_MM_SS);
        barLuck.setCreateTime(currentTime);
        User currentUser = (User) ThreadlocalUtils.getCache(ThreadLocalConstants.USER_INFO);
        barLuck.setCreateUser(currentUser.getId());
        barLuckDao.insert(barLuck);
        if (StringUtils.isEmpty(subInfo)) {
            log.info("未选择附属信息");
            return;
        }
        Map<String, Object> queryParam = new HashMap<>();
        queryParam.put("luckId", mainId);
        queryParam.put("startNum", 0);
        queryParam.put("pageSize", 1000);
        if (StringUtils.contains(subInfo, "1")) {
            List<BarLuckAudience> luckAudienceList = barLuckAudienceDao.pageBarLuckAudienceList(queryParam);
            if (!ObjectUtils.isEmpty(luckAudienceList)) {
                List<BarLuckAudience> newLuckAudienceList = new ArrayList<>(luckAudienceList.size());
                for (BarLuckAudience audience : luckAudienceList) {
                    audience.setId(SnowFlake.nextIdStr());
                    audience.setLuckId(id);
                    newLuckAudienceList.add(audience);
                }
                barLuckAudienceDao.insertBatch(newLuckAudienceList);
            }
        }
        if (StringUtils.contains(subInfo, "2")) {
            List<BarLuckAward> luckAwardList = barLuckAwardDao.pageBarLuckAwardList(queryParam);
            if (!ObjectUtils.isEmpty(luckAwardList)) {
                List<BarLuckAward> newLuckAwardList = new ArrayList<>(luckAwardList.size());
                for (BarLuckAward award : luckAwardList) {
                    award.setId(SnowFlake.nextIdStr());
                    award.setLuckId(id);
                    award.setLeftNum(award.getNum());
                    newLuckAwardList.add(award);
                }
                barLuckAwardDao.insertBatch(newLuckAwardList);
            }
        }
    }

    @Override
    public void exportLuckInfo(HttpServletResponse response) {
        ExcelData data = new ExcelData();
        data.setFileName("论坛活动导出excel.xlsx");
        Map<String, Map<String, String>> userMap = userDao.getUserMap();
        Map<String, Object> query = new HashMap<>();
        query.put("startNum", 0);
        query.put("pageSize", 10000);
        List<BarLuck> luckList = barLuckDao.pageBarLuckList(query);
        String[] head = {"序号", "活动名称", "活动类型", "投放方式", "状态", "开始时间", "结束时间", "奖品总数量", "奖品剩余数量", "创建人", "创建时间", "修改人", "修改时间"};
        data.setHead(head);
        String[][] dataList = new String[luckList.size()][head.length];
        int index = 0;
        for (BarLuck item : luckList) {
            dataList[index][0] = String.valueOf(index + 1);
            dataList[index][1] = item.getTitle();
            dataList[index][2] = item.getType() == 0 ? "抽奖" : "兑换";
            dataList[index][3] = item.getDelivery() == 0 ? "全员" : "定向";
            dataList[index][4] = item.getStatus() == 1 ? "已启用" : "未启用";
            dataList[index][5] = item.getStartTime();
            dataList[index][6] = item.getEndTime();
            dataList[index][7] = String.valueOf(item.getTotalNum());
            dataList[index][8] = String.valueOf(item.getLeftNum());
            dataList[index][9] = userMap.containsKey(item.getCreateUser()) ? userMap.get(item.getCreateUser()).get("name") : "--";
            dataList[index][10] = item.getCreateTime();
            dataList[index][11] = userMap.containsKey(item.getUpdateUser()) ? userMap.get(item.getUpdateUser()).get("name") : "--";
            dataList[index][12] = item.getUpdateTime();
            index++;
        }
        data.setData(Arrays.asList(dataList));
        ExcelUtils.exportExcel(response, data);
    }

    @Override
    public Map<String, Object> startLottery(String luckId) {
        User current = (User) ThreadlocalUtils.getCache(ThreadLocalConstants.USER_INFO);
        String userId = current.getId();
        // 获取用户抽奖信息
        Map<String, Object> opportunityMap = barLuckDetailService.queryParticipationTime(userId, luckId);
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("awardTitle", "空气");
        resultMap.put("index", 0);
        BarLuck barLuck = barLuckDao.selectById(luckId);
        String currentTime = TimeUtils.getDate(TimeFormatConstants.YYYY_MM_DD_HH_MM_SS);
        // 判断是否为消耗积分活动
        if (barLuck.getType() == 0) {
            if (barLuck.getJoinType() == 1) {
                BarPointsFlow pointsFlow = new BarPointsFlow();
                pointsFlow.setId(SnowFlake.nextIdStr());
                pointsFlow.setUserId(userId);
                pointsFlow.setPoints(barLuck.getJoinThreshold());
                pointsFlow.setOperateTime(currentTime);
                pointsFlow.setType(2);
                pointsFlow.setSourceType(BarPointSourceEnum.CONSUMPTION.getValue());
                barPointsFlowDao.insert(pointsFlow);
            }
        }
        // 活动参与记录
        BarLuckFlow barLuckFlow = new BarLuckFlow();
        barLuckFlow.setLuckId(luckId);
        barLuckFlow.setHit(0);
        barLuckFlow.setOperateTime(currentTime);
        barLuckFlow.setUserId(userId);
        barLuckFlow.setUserName(current.getName());
        barLuckFlow.setId(SnowFlake.nextIdStr());
        if (ObjectUtils.isEmpty(opportunityMap) || !StringUtils.equals((String) opportunityMap.get("luckId"), luckId)) {
            barLuckFlowDao.insert(barLuckFlow);
            return resultMap;
        }
        // 定向活动判断用户是否具备资格
        if (barLuck.getDelivery() == 1) {
            BarLuckAudience barLuckAudience = barLuckAudienceDao.getBarLuckOpportunity(luckId, userId);
            if (ObjectUtils.isEmpty(barLuckAudience) || barLuckAudience.getWinningRate() == 0) {
                barLuckFlowDao.insert(barLuckFlow);
                return resultMap;
            }
        }
        // 计算奖品
        resultMap = getAwardHit(barLuck, userId, currentTime, barLuckFlow);
        return resultMap;
    }

    @Override
    public List<Map<String, Object>> pageLuckFlow(Map<String, Object> queryParam) {
        int startNum = (int) queryParam.get("startNum");
        List<BarLuckFlow> originalList = barLuckFlowDao.pageBarLuckFlowList(queryParam);
        return completeFlowResult(originalList, startNum);
    }

    @Override
    public int getLuckFlowCount(Map<String, Object> queryParam) {
        return barLuckFlowDao.getBarLuckFlowCount(queryParam);
    }

    /**
     * 计算用户中奖信息
     *
     * @return 用户中奖信息
     */
    private Map<String, Object> getAwardHit(BarLuck barLuck, String userId, String currentTime, BarLuckFlow barLuckFlow) {
        String luckId = barLuck.getId();
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("awardTitle", "空气");
        resultMap.put("index", 0);
        BarPresent floorPresent = null;
        if (barLuck.getFloor() == 1) {
            floorPresent = barPresentDao.selectById(barLuck.getFloorAward());
            resultMap.put("awardTitle", floorPresent.getName());
        }
        List<BarLuckAward> luckAwardList = barLuckAwardDao.getLuckAwardList(luckId);
        List<BarLuckAward> availableList = luckAwardList.stream().filter(award -> award.getLeftNum() > 0).collect(Collectors.toList());
        if (ObjectUtils.isEmpty(availableList)) {
            if (barLuck.getFloor() == 1) {
                barLuckFlow.setHit(1);
                barLuckFlow.setPresentId(barLuck.getFloorAward());
            }
            barLuckFlowDao.insert(barLuckFlow);
            return resultMap;
        }
        int winningNum = (int) (Math.random() * 100 + 1);
        int rateNum = 0;
        int rand = 0;
        boolean hitFlag = false;
        BarLuckAward award = null;
        for (BarLuckAward awardItem : availableList) {
            rateNum = rateNum + awardItem.getWinningRate();
            rand++;
            if (!(winningNum > rateNum)) {
                hitFlag = true;
                award = awardItem;
                break;
            }
        }
        if (!hitFlag) {
            if (barLuck.getFloor() == 1) {
                barLuckFlow.setHit(1);
                barLuckFlow.setPresentId(barLuck.getFloorAward());
                barPointsService.updateUserPoints(userId, BarPointBusinessEnum.LUCK_GIFT.getValue(),floorPresent.getPointNum());
            }
            barLuckFlowDao.insert(barLuckFlow);
            return resultMap;
        }
        barLuckFlow.setPresentId(award.getPresentId());
        barLuckFlow.setHit(1);
        barLuckFlowDao.insert(barLuckFlow);
        award.setLeftNum(award.getLeftNum() - 1);
        barLuckAwardDao.updateById(award);
        BarPresentFlow presentFlow = new BarPresentFlow();
        presentFlow.setId(SnowFlake.nextIdStr());
        presentFlow.setLuckId(luckId);
        presentFlow.setPresentId(award.getPresentId());
        presentFlow.setUserId(userId);
        presentFlow.setOperateTime(currentTime);
        barPresentFlowDao.insert(presentFlow);
        resultMap.put("awardTitle", "抽中奖品");
        resultMap.put("index", rand);
        return resultMap;
    }

    /**
     * 数据补全转换
     *
     * @param originList 原始数据列表
     * @param startNum   起始查询数
     * @return 补全数据
     */
    private List<Map<String, Object>> completeResult(List<BarLuck> originList, int startNum) {
        if (ObjectUtils.isEmpty(originList)) {
            return new ArrayList<>();
        }
        List<Map<String, Object>> resultList = new ArrayList<>(originList.size());
        int sort = startNum + 1;
        for (BarLuck record : originList) {
            Map<String, Object> map = ObjectUtils.object2Map(record);
            map.put("sort", sort);
            resultList.add(map);
            sort++;
        }
        return resultList;
    }

    /**
     * 数据补全转换
     *
     * @param originList 原始数据列表
     * @param startNum   起始查询数
     * @return 补全数据
     */
    private List<Map<String, Object>> completeFlowResult(List<BarLuckFlow> originList, int startNum) {
        if (ObjectUtils.isEmpty(originList)) {
            return new ArrayList<>();
        }
        List<Map<String, Object>> resultList = new ArrayList<>(originList.size());
        Map<String,Map<String,String>> presentMap = barPresentDao.queryPresentMap();
        int sort = startNum + 1;
        for (BarLuckFlow record : originList) {
            Map<String, Object> map = ObjectUtils.object2Map(record);
            map.put("sort", sort);
            if (record.getHit() == 1) {
                map.put("presentName", presentMap.containsKey(record.getPresentId()) ? presentMap.get(record.getPresentId()).get("name") : "");
            }
            resultList.add(map);
            sort++;
        }
        return resultList;
    }
}
